package organizer;

import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.DefaultListModel;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JProgressBar;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JToolBar;
import javax.swing.SwingUtilities;
import javax.swing.event.ListDataEvent;
import javax.swing.event.ListDataListener;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableCellRenderer;
import org.apache.log4j.Logger;

/**
 * Client-side (gui) part of organizer.
 * @author dawong
 */
public class OrganizerUI extends JFrame {

    private static final Logger log = Logger.getLogger(OrganizerUI.class);
    
    private JButton scanButton;
    private JButton addScanDirButton;
    private JButton removeScanDirButton;
    private JTable table;
    private JList dirList;
    private JProgressBar progressBar;
    private JButton cancelButton;
    private JDialog rulesDialog;
    private RulesComponent rulesComponent;
    private JLabel trashDirectoryLabel;
    private JFileChooser scanDirFileChooser;
    private JFileChooser trashDirFileChooser;
    private JFileChooser exportFileChooser;
    private JButton selectTrashButton;
    private JButton removeButton;
    private JButton rulesButton;
    private JButton exportButton;
    private JMenuItem exportAllButton;
    private JMenuItem exportSelectedButton;
    
    private DuplicateTableModel tableModel;
    private DefaultListModel dirListModel;

    private Organizer organizer;
    private boolean canceled;
    private Executor executor;

    public OrganizerUI() {
        super("Organizer");
        organizer = new Organizer();
        tableModel = new DuplicateTableModel();
        dirListModel = new DefaultListModel();
        executor = Executors.newSingleThreadExecutor();
        initComponents();
    }

    private void initComponents() {
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        JPanel contentPane = new JPanel();
        contentPane.setLayout(new GridBagLayout());
        setContentPane(contentPane);

        GridBagConstraints c;

        JPanel selectPane = new JPanel();
        selectPane.setLayout(new GridBagLayout());
        c = new GridBagConstraints();
        c.weightx = 1;
        c.fill = GridBagConstraints.HORIZONTAL;
        c.gridwidth = GridBagConstraints.REMAINDER;
        contentPane.add(selectPane, c);

        JLabel label = new JLabel("Scan Dirs");
        c = new GridBagConstraints();
        selectPane.add(label, c);

        dirListModel.addListDataListener(new ListDataListener() {

            public void intervalAdded(ListDataEvent e) {
                updateEnabled();
            }

            public void intervalRemoved(ListDataEvent e) {
                updateEnabled();
            }

            public void contentsChanged(ListDataEvent e) {
                updateEnabled();
            }
            
            private void updateEnabled() {
                scanButton.setEnabled(!dirListModel.isEmpty());
            }
        });
        
        dirList = new JList(dirListModel);
        dirList.getSelectionModel().addListSelectionListener(new ListSelectionListener() {

            public void valueChanged(ListSelectionEvent e) {
                if (!e.getValueIsAdjusting()) {
                    removeScanDirButton.setEnabled(dirList.getSelectedIndices().length > 0);
                }
            }
        });
        c = new GridBagConstraints();
        c.weightx = 1;
        c.fill = GridBagConstraints.HORIZONTAL;
        JScrollPane scrollPane = new JScrollPane(dirList);
        scrollPane.setPreferredSize(new Dimension(200, 200));
        selectPane.add(scrollPane, c);

        scanButton = new JButton("Scan");
        scanButton.setEnabled(false);
        scanButton.addActionListener(new ActionListener() {

            public void actionPerformed(ActionEvent e) {
                scanButtonActionPerformed();
            }
        });
        scanButton.setToolTipText("Scan directories for duplicates");
        c = new GridBagConstraints();
        c.fill = GridBagConstraints.BOTH;
        c.gridwidth = GridBagConstraints.REMAINDER;
        c.gridheight = 2;
        selectPane.add(scanButton, c);

        c = new GridBagConstraints();
        selectPane.add(Box.createGlue(), c);

        JPanel selectButtonPane = new JPanel();
        c = new GridBagConstraints();
        c.fill = GridBagConstraints.HORIZONTAL;
        selectPane.add(selectButtonPane, c);

        addScanDirButton = new JButton("Add..");
        addScanDirButton.addActionListener(new ActionListener() {

            public void actionPerformed(ActionEvent e) {
                addScanDirButtonActionPerformed();
            }
        });
        addScanDirButton.setToolTipText("Add directories to scan");
        c = new GridBagConstraints();
        selectButtonPane.add(addScanDirButton, c);

        removeScanDirButton = new JButton("Remove");
        removeScanDirButton.setEnabled(false);
        removeScanDirButton.addActionListener(new ActionListener() {

            public void actionPerformed(ActionEvent e) {
                removeScanDirButtonActionPerformed();
            }
        });
        removeScanDirButton.setToolTipText("Remove selected directories");
        c = new GridBagConstraints();
        c.gridwidth = GridBagConstraints.REMAINDER;
        selectButtonPane.add(removeScanDirButton, c);

        JPanel monitorPane = new JPanel();
        monitorPane.setLayout(new GridBagLayout());
        c = new GridBagConstraints();
        c.weightx = 1;
        c.fill = GridBagConstraints.HORIZONTAL;
        c.gridwidth = GridBagConstraints.REMAINDER;
        contentPane.add(monitorPane, c);

        progressBar = new JProgressBar();
        c = new GridBagConstraints();
        c.weightx = 1;
        c.fill = GridBagConstraints.HORIZONTAL;
        monitorPane.add(progressBar, c);

        cancelButton = new JButton("Cancel");
        cancelButton.setEnabled(false);
        cancelButton.addActionListener(new ActionListener() {

            public void actionPerformed(ActionEvent e) {
                cancelButtonActionPerformed();
            }
        });
        c = new GridBagConstraints();
        c.gridwidth = GridBagConstraints.REMAINDER;
        monitorPane.add(cancelButton, c);

        JToolBar toolbar = new JToolBar();
        toolbar.setFloatable(false);
        c = new GridBagConstraints();
        c.weightx = 1;
        c.fill = GridBagConstraints.HORIZONTAL;
        c.gridwidth = GridBagConstraints.REMAINDER;
        contentPane.add(toolbar, c);
        
        rulesButton = new JButton("Rules..");
        rulesButton.setEnabled(false);
        rulesButton.addActionListener(new ActionListener() {

            public void actionPerformed(ActionEvent e) {
                rulesButtonActionPerformed();
            }
        });
        rulesButton.setToolTipText("Set the order in which the files-to-keep are selected");
        toolbar.add(rulesButton);
        
        final JPopupMenu popup = new JPopupMenu();

        exportButton = new JButton("Export");
        exportButton.setEnabled(false);
        exportButton.setToolTipText("Save the selected entries to a file.");
        exportButton.addActionListener(new ActionListener() {
            
            public void actionPerformed(ActionEvent e) {
                popup.show(exportButton, 0, exportButton.getHeight());
            }
        });
        toolbar.add(exportButton);

        exportSelectedButton = new JMenuItem("Selected Only..");
        exportSelectedButton.setToolTipText("Export only the selected rows");
        exportSelectedButton.addActionListener(new ActionListener() {

            public void actionPerformed(ActionEvent e) {
                exportSelectedButtonActionPerformed();
            }
        });
        popup.add(exportSelectedButton);
        
        exportAllButton = new JMenuItem("All..");
        exportAllButton.setToolTipText("Export all rows in table");
        exportAllButton.addActionListener(new ActionListener() {

            public void actionPerformed(ActionEvent e) {
                exportAllButtonActionPerformed();
            }
        });
        popup.add(exportAllButton);
        
        table = new JTable(tableModel);
        table.addMouseListener(new MouseAdapter() {

            @Override
            public void mouseClicked(MouseEvent e) {
                if (e.getComponent().isEnabled() && e.getButton() == MouseEvent.BUTTON1 &&
                        e.getClickCount() == 2) {
                    Point p = e.getPoint();
                    int viewRow = table.rowAtPoint(p);
                    int modelRow = table.convertRowIndexToModel(viewRow);
                    DuplicateRow dupRow = tableModel.getRow(modelRow);
                    try {
                        File f = new File(dupRow.getPath());
                        String[] cmd = new String[] { "/usr/bin/shotwell", dupRow.getPath() };
                        Runtime.getRuntime().exec(cmd);
                    } catch (IOException ex) {
                        log.error(ex);
                    }
                }

            }
        });
        table.getColumnModel().getColumn(0).setMaxWidth(15);
        TableCellRenderer defaultBooleanRenderer = table.getDefaultRenderer(Boolean.class);
        table.setDefaultRenderer(Boolean.class, 
                new AlternateGroupTableCellRenderer(defaultBooleanRenderer));
        TableCellRenderer defaultStringRenderer = table.getDefaultRenderer(String.class);
        table.setDefaultRenderer(String.class,
                new AlternateGroupTableCellRenderer(defaultStringRenderer));
        c = new GridBagConstraints();
        c.gridwidth = GridBagConstraints.REMAINDER;
        c.fill = GridBagConstraints.BOTH;
        c.weightx = 1;
        c.weighty = 1;
        contentPane.add(new JScrollPane(table), c);

        JPanel trashPane = new JPanel();
        trashPane.setLayout(new GridBagLayout());
        c = new GridBagConstraints();
        c.fill = GridBagConstraints.HORIZONTAL;
        c.weightx = 1;
        c.gridwidth = GridBagConstraints.REMAINDER;
        contentPane.add(trashPane, c);

        JLabel trashLabel = new JLabel("Trash Dir");
        c = new GridBagConstraints();
        trashPane.add(trashLabel, c);

        trashDirectoryLabel = new JLabel(" ");
        trashDirectoryLabel.setBorder(BorderFactory.createLineBorder(Color.black));
        c = new GridBagConstraints();
        c.weightx = 1;
        c.fill = GridBagConstraints.HORIZONTAL;
        trashPane.add(trashDirectoryLabel, c);

        selectTrashButton = new JButton("Select");
        selectTrashButton.addActionListener(new ActionListener() {

            public void actionPerformed(ActionEvent e) {
                selectTrashButtonActionPerformed();
            }
        });
        selectTrashButton.setToolTipText("Select a trash dir");
        c = new GridBagConstraints();
        trashPane.add(selectTrashButton, c);

        removeButton = new JButton("Trash");
        removeButton.setEnabled(false);
        removeButton.addActionListener(new ActionListener() {

            public void actionPerformed(ActionEvent e) {
                removeButtonActionPerformed();
            }
        });
        removeButton.setEnabled(false);
        removeButton.setToolTipText("Move selected files to the trash dir");
        c.gridwidth = GridBagConstraints.REMAINDER;
        trashPane.add(removeButton, c);
        
        // Not added to the gui yet.  Created to be wrapped by a dialog later.
        rulesComponent = new RulesComponent();
        rulesComponent.addActionListener(new ActionListener() {

            public void actionPerformed(ActionEvent e) {
                String cmd = e.getActionCommand();
                if (cmd.equals("apply")) {
                    applyRulesButtonActionPerformed();
                }
            }
        });
    }
    
    private synchronized void setCanceled(boolean canceled) {
        this.canceled = canceled;
    }

    private synchronized boolean isCanceled() {
        return canceled;
    }

    private void removeScanDirButtonActionPerformed() {
        int[] selected = dirList.getSelectedIndices();
        for (int i = selected.length - 1; i >= 0; i--) {
            dirListModel.remove(selected[i]);
        }
    }

    private void selectTrashButtonActionPerformed() {
        if (trashDirFileChooser == null) {
            trashDirFileChooser = new JFileChooser();
            trashDirFileChooser.setMultiSelectionEnabled(false);
            trashDirFileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
        }
        int opt = trashDirFileChooser.showDialog(this, "Select");
        if (opt == JFileChooser.APPROVE_OPTION) {
            File file = trashDirFileChooser.getSelectedFile();
            if (file != null) {
                organizer.setTrashDir(file);
                trashDirectoryLabel.setText(file.getAbsolutePath());
            }
        }
        removeButton.setEnabled(organizer.getTrashDir() != null);
    }

    private void applyRulesButtonActionPerformed() {
        List<String> dirs = rulesComponent.getDirectories();
        Set<String> deletes = organizer.selectDuplicates(dirs);
        tableModel.selectDeleted(deletes);
    }

    private void rulesButtonActionPerformed() {
        if (rulesDialog == null) {
            rulesDialog = new JDialog(this, "Rules", false);
            rulesDialog.setContentPane(rulesComponent);
            rulesDialog.pack();
            rulesDialog.setLocationRelativeTo(this);
        }
        rulesDialog.setVisible(true);
    }
    
    private JFileChooser getExportFileChooser() {
        if (exportFileChooser == null) {
            exportFileChooser = new JFileChooser();
            exportFileChooser.setMultiSelectionEnabled(false);
            exportFileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
        }
        return exportFileChooser;
    }
    
    private void exportSelectedButtonActionPerformed() {
        JFileChooser fileChooser = getExportFileChooser();
        int opt = fileChooser.showSaveDialog(this);
        if (opt == JFileChooser.APPROVE_OPTION) {
            File file = fileChooser.getSelectedFile();
            if (file != null) {
                if (file.exists()) {
                    int overwriteOpt = JOptionPane.showConfirmDialog(OrganizerUI.this, "File exists.  Overwrite?");
                    if (overwriteOpt == JOptionPane.OK_OPTION) {
                        exportSelected(file);
                    }
                } else {
                    exportSelected(file);
                }
            }
        }
    }
    
    /**
     * Write the selected duplicates to {@code exportFile}.
     * @param exportFile 
     */
    private void exportSelected(File exportFile) {
        List<DuplicateRow> deleteRows = tableModel.getDeleted();
        List<String> paths = new ArrayList<String>();
        for (DuplicateRow row : deleteRows) {
            paths.add(row.getPath());
        }
        organizer.exportResults(exportFile, paths);
    }
    
    private void exportAllButtonActionPerformed() {
        JFileChooser fileChooser = getExportFileChooser();
        int opt = fileChooser.showSaveDialog(this);
        if (opt == JFileChooser.APPROVE_OPTION) {
            File file = fileChooser.getSelectedFile();
            if (file != null) {
                if (file.exists()) {
                    int overwriteOpt = JOptionPane.showConfirmDialog(OrganizerUI.this, "File exists.  Overwrite?");
                    if (overwriteOpt == JOptionPane.OK_OPTION) {
                        exportAll(file);
                    }
                } else {
                    exportAll(file);
                }
            }
        }
    }
    
    /**
     * Write all duplicates to {@code exportFile}.
     * @param exportFile 
     */
    private void exportAll(File exportFile) {
        List<DuplicateRow> allRows = tableModel.rows;
        List<String> paths = new ArrayList<String>();
        for (DuplicateRow row : allRows) {
            paths.add(row.getPath());
        }
        organizer.exportResults(exportFile, paths);
    }
    
    private void cancelButtonActionPerformed() {
        setCanceled(true);
    }

    private void removeButtonActionPerformed() {
        List<DuplicateRow> deletedRows = tableModel.getDeleted();
        Set<String> deletes = new HashSet<String>();
        for (DuplicateRow row : deletedRows) {
            deletes.add(row.getPath());
        }
        organizer.removeDuplicates(deletes);
        JOptionPane.showMessageDialog(this, "Moved " + deletes.size() + " files");
    }

    private void scanButtonActionPerformed() {
        setGuiEnabled(false);
        executor.execute(new Runnable() {

            public void run() {
                SwingUtilities.invokeLater(new Runnable() {

                    public void run() {
                        tableModel.clear();
                    }
                });
                List<File> scanDirs = new ArrayList<File>();
                int size = dirList.getModel().getSize();
                for (int i = 0; i < size; i++) {
                    String scanPath = (String) dirList.getModel().getElementAt(i);
                    scanDirs.add(new File(scanPath));
                }
                ProgressMonitor progress = new ProgressMonitor();
                organizer.setSourceDirs(scanDirs);
                organizer.scanFiles(progress);
                organizer.calcDuplicates(progress);
                final List<Duplicate> dups = organizer.getDuplicates(progress);
                log.debug("sorting dups");
                Collections.sort(dups, new Comparator<Duplicate>() {

                    public int compare(Duplicate o1, Duplicate o2) {
                        String f1 = o1.getFiles().iterator().next();
                        String f2 = o2.getFiles().iterator().next();
                        return f1.compareTo(f2);
                    }
                });
                final List<String> dupDirs = organizer.getDuplicateDirectories();
                SwingUtilities.invokeLater(new Runnable() {

                    public void run() {
                        rulesComponent.setDirectories(dupDirs);
                        log.debug("selectings dups");
                        Set<String> deletes = organizer.selectDuplicates(dupDirs);
                        log.debug("displaying dups");
                        boolean isOdd = true;
                        for (Duplicate dup : dups) {
                            String checksum = dup.getChecksum();
                            for (String file : dup.getFiles()) {
                                DuplicateRow row = new DuplicateRow(false, file, checksum, isOdd);
                                tableModel.addRow(row);
                            }
                            isOdd = !isOdd;
                        }
                        tableModel.selectDeleted(deletes);
                        setGuiEnabled(true);
                    }
                });
            }
        });
    }

    private void addScanDirButtonActionPerformed() {
        if (scanDirFileChooser == null) {
            scanDirFileChooser = new JFileChooser();
            scanDirFileChooser.setMultiSelectionEnabled(true);
            scanDirFileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
        }
        int opt = scanDirFileChooser.showDialog(this, "Add");
        if (opt == JFileChooser.APPROVE_OPTION) {
            File[] files = scanDirFileChooser.getSelectedFiles();
            if (files != null) {
                for (File file : files) {
                    dirListModel.addElement(file.getAbsolutePath());
                }
            } else {
                File file = scanDirFileChooser.getSelectedFile();
                if (file != null) {
                    dirListModel.addElement(file.getAbsolutePath());
                }
            }
        }
    }
    
    private void setGuiEnabled(boolean enable) {
        if (enable) {
            scanButton.setEnabled(!dirListModel.isEmpty());
        } else {
            scanButton.setEnabled(false);
        }
        addScanDirButton.setEnabled(enable);
        rulesButton.setEnabled(enable);
        exportButton.setEnabled(enable);
        selectTrashButton.setEnabled(enable);
        if (enable) {
            if (organizer.getTrashDir() != null)
                removeButton.setEnabled(true);
            else
                removeButton.setEnabled(false);
        } else {
            removeButton.setEnabled(false);
        }
        cancelButton.setEnabled(!enable);
    }
    
    private class ProgressMonitor implements IProgressMonitor {
        
        private String message = null;

        public void start(final int size) {
            SwingUtilities.invokeLater(new Runnable() {

                public void run() {
                    progressBar.setMaximum(size);
                    progressBar.setValue(0);
                    OrganizerUI.this.setCanceled(false);
                    progressBar.setStringPainted(true);
                }
            });
        }

        public void increment(final int incr) {
            SwingUtilities.invokeLater(new Runnable() {

                public void run() {
                    progressBar.setValue(progressBar.getValue() + incr);
                    StringBuilder sb = new StringBuilder();
                    if (message != null) {
                        sb.append(message);
                        sb.append(" ");
                    }
                    sb.append(progressBar.getValue());
                    sb.append(" / ");
                    sb.append(progressBar.getMaximum());
                    progressBar.setString(sb.toString());
                }
            });
        }

        public void stop() {
            message = null;
            SwingUtilities.invokeLater(new Runnable() {

                public void run() {
                    progressBar.setStringPainted(false);
                }
            });
        }

        public boolean isCanceled() {
            return OrganizerUI.this.isCanceled();
        }

        public void showMessage(String message) {
            this.message = message;
        }

    }
    
    private static final class DuplicateTableModel extends AbstractTableModel {

        private List<DuplicateRow> rows;

        public DuplicateTableModel() {
            rows = new ArrayList<DuplicateRow>();
        }

        private String[] COLUMN_NAME = new String[] { "X", "File" };

        @Override
        public String getColumnName(int column) {
            if (column < COLUMN_NAME.length)
                return COLUMN_NAME[column];
            return super.getColumnName(column);
        }
        
        @Override
        public boolean isCellEditable(int row, int column) {
            return (column == 0);
        }

        private Class[] COLUMN_CLASS = new Class[] { Boolean.class, String.class };

        @Override
        public Class<?> getColumnClass(int columnIndex) {
            return COLUMN_CLASS[columnIndex];
        }

        public int getRowCount() {
            return rows.size();
        }

        public int getColumnCount() {
            return 2;
        }

        public Object getValueAt(int rowIndex, int columnIndex) {
            DuplicateRow row = rows.get(rowIndex);
            switch (columnIndex) {
                case 0:
                    return row.isDelete();
                case 1:
                    return row.getPath();
            }
            return null;
        }

        @Override
        public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
            DuplicateRow row = rows.get(rowIndex);
            switch (columnIndex) {
                case 0:
                    row.setDelete((Boolean)aValue);
                    fireTableCellUpdated(rowIndex, 0);
                    fireTableCellUpdated(rowIndex, 1);
                    break;
            }
        }

        public void addRow(DuplicateRow row) {
            int rowIndex = rows.size();
            rows.add(row);
            fireTableRowsInserted(rowIndex, rowIndex);
        }

        public DuplicateRow getRow(int row) {
            return rows.get(row);
        }

        public void selectDeleted(Set<String> deletes) {
            for (DuplicateRow row : rows) {
                row.setDelete(deletes.contains(row.getPath()));
            }
            fireTableDataChanged();
        }
        
        public List<DuplicateRow> getDeleted() {
            List<DuplicateRow> deleted = new ArrayList<DuplicateRow>();
            for (DuplicateRow row : rows) {
                if (row.isDelete())
                    deleted.add(row);
            }
            return deleted;
        }

        public void clear() {
            rows.clear();
            fireTableDataChanged();
        }
        
    }

    private static final class DuplicateRow {
        private boolean delete;
        private String path;
        private String checksum;
        boolean odd = false;

        public DuplicateRow(boolean delete, String path, String checksum, boolean odd) {
            this.delete = delete;
            this.path = path;
            this.checksum = checksum;
            this.odd = odd;
        }

        public String getChecksum() {
            return checksum;
        }

        public String getPath() {
            return path;
        }

        public boolean isDelete() {
            return delete;
        }

        public void setDelete(boolean delete) {
            this.delete = delete;
        }

        public boolean isOdd() {
            return odd;
        }

    }

    private static class AlternateGroupTableCellRenderer implements TableCellRenderer {

        private TableCellRenderer defaultRenderer;

        public AlternateGroupTableCellRenderer(TableCellRenderer defaultRenderer) {
            this.defaultRenderer = defaultRenderer;
        }

        public Component getTableCellRendererComponent(JTable table, Object value,
                boolean isSelected, boolean hasFocus, int row, int column) {
            Component comp = defaultRenderer.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
            DuplicateTableModel model = (DuplicateTableModel) table.getModel();
            DuplicateRow dup = model.getRow(row);
            if (dup.isDelete()) {
                comp.setForeground(Color.red);
            } else {
                comp.setForeground(Color.black);
            }
            if (!dup.isOdd()) {
                comp.setBackground(Color.lightGray);
            } else {
                comp.setBackground(Color.white);
            }
            return comp;
        }

    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {

            public void run() {
                OrganizerUI ui = new OrganizerUI();
                ui.pack();
                ui.setLocationRelativeTo(null);
                ui.setVisible(true);
            }
        });
    }

}
